package main

import (
	"crypto"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"math/big"
	"reflect"
	"time"
)

func main() {
	mcert, mcaKey, err := LoadPair("./mca/mca.cert.pem", "./mca/mca.key.pem")
	if err != nil {
		return
	}

	//loc := time.FixedZone("Oz/Atlantis", int((2 * time.Hour).Seconds()))

	//now := time.Unix(1000, 0).In(loc)
	//nowUTC := now.UTC()
	//expiry := time.Unix(10000, 0)
	now := time.Now()
	expiry := time.Now().Add(time.Hour * 24 * 31)

	// 吊销的证书列表，应该持久化保存
	revokedCerts := []pkix.RevokedCertificate{
		{
			SerialNumber:   big.NewInt(1661503495),
			RevocationTime: now,
		},
	}

	crlBytes, err := getCrlData(mcert, mcaKey, revokedCerts, now, expiry)
	if err != nil {
		return
	}

	// 校验，可忽略
	expectedCerts := []pkix.RevokedCertificate{
		{
			// 1661503495 对应证书c107.cert.pem
			SerialNumber:   big.NewInt(1661503495),
			RevocationTime: now,
		},
	}
	parsedCRL, err := x509.ParseDERCRL(crlBytes)
	if err != nil {
		fmt.Errorf("error reparsing CRL: %s", err)
	}
	if !reflect.DeepEqual(parsedCRL.TBSCertList.RevokedCertificates, expectedCerts) {
		fmt.Errorf("RevokedCertificates mismatch: got %v; want %v.",
			parsedCRL.TBSCertList.RevokedCertificates, expectedCerts)
	}

}

// cert 证书对象
func getCrlData(cert *x509.Certificate, priv interface{}, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) {
	// 记录吊销列表revokedCerts的最新值，在有效期内，有变更就更新crl文件，无变更则返回本地保存的crl文件

	// root ca的crl更新时，采用主动的方式，所以过期时间尽可能长
	//crlBytes, err := mcert.CreateCRL(rand.Reader, mcaKey, revokedCerts, time.Now(), time.Now().Add(time.Hour*24*365*100))
	// mca的crl更新时，采用主动的方式，所以过期时间尽可能长
	//cert := &x509.Certificate{}
	crlBytes, err = cert.CreateCRL(rand.Reader, priv, revokedCerts, time.Now(), expiry)
	if err != nil {
		fmt.Errorf("error creating CRL: %s", err)
		return nil, err
	}

	// 保存CRL文件（非必须操作，优化项）
	pemData := pem.EncodeToMemory(&pem.Block{
		Type:  "X509 CRL",
		Bytes: crlBytes,
	})
	if err = ioutil.WriteFile("./mca/crl.pem", pemData, 0644); err != nil {
		panic(err)
	}

	return
}

// LoadPair 从PEM文件读取证书
func LoadPair(certFile, keyFile string) (cert *x509.Certificate, privateKey crypto.PrivateKey, err error) {
	if len(certFile) == 0 && len(keyFile) == 0 {
		return nil, nil, errors.New("cert or key has not provided")
	}

	// load cert and key by tls.LoadX509KeyPair
	tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return
	}
	// 返回私钥
	privateKey = tlsCert.PrivateKey

	cert, err = x509.ParseCertificate(tlsCert.Certificate[0])
	return
}
